package org.example.demo1.service;

import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@Service
@Slf4j
public class SSHTerminalService {

    // 定义SSH连接的空闲超时时间，单位毫秒
    // 每次活动（发送命令或接收输出）都会重置此计时器
    private static final long IDLE_TIMEOUT_MS = 120 * 1000; // 设置为2分钟空闲超时

    // 定义Keep-alive线程的检查间隔，单位毫秒
    private static final long KEEP_ALIVE_INTERVAL_MS = 30 * 1000; // 每30秒检查一次

    // 新增：连续心跳失败的最大允许次数
    private static final int MAX_FAILED_KEEP_ALIVE_ATTEMPTS = 3; // 允许连续失败3次

    private final Map<String, SSHConnection> connections = new ConcurrentHashMap<>();

    /**
     * SSH连接信息 - 增强版
     */
    public static class SSHConnection {
        private Session session;
        private ChannelShell channel;
        private InputStream inputStream;
        private OutputStream outputStream;
        private WebSocketSession webSocketSession;
        private Thread outputThread;
        //private Thread keepAliveThread;
        private boolean connected = false;
        private AtomicLong lastActivity = new AtomicLong(System.currentTimeMillis()); // 最后活动时间
        private volatile int failedKeepAliveAttempts = 0; // 新增：记录连续心跳失败次数

        // getters and setters
        public Session getSession() { return session; }
        public void setSession(Session session) { this.session = session; }
        public ChannelShell getChannel() { return channel; }
        public void setChannel(ChannelShell channel) { this.channel = channel; }
        public InputStream getInputStream() { return inputStream; }
        public void setInputStream(InputStream inputStream) { this.inputStream = inputStream; }
        public OutputStream getOutputStream() { return outputStream; }
        public void setOutputStream(OutputStream outputStream) { this.outputStream = outputStream; }
        public WebSocketSession getWebSocketSession() { return webSocketSession; }
        public void setWebSocketSession(WebSocketSession webSocketSession) { this.webSocketSession = webSocketSession; }
        public Thread getOutputThread() { return outputThread; }
        public void setOutputThread(Thread outputThread) { this.outputThread = outputThread; }
        //public Thread getKeepAliveThread() { return keepAliveThread; }
        //public void setKeepAliveThread(Thread keepAliveThread) { this.keepAliveThread = keepAliveThread; }
        public boolean isConnected() { return connected; }
        public void setConnected(boolean connected) { this.connected = connected; }
        public AtomicLong getLastActivity() { return lastActivity; }
        public void setLastActivity(AtomicLong lastActivity) { this.lastActivity = lastActivity; }
        public int getFailedKeepAliveAttempts() { return failedKeepAliveAttempts; } // 新增 getter
        public void setFailedKeepAliveAttempts(int failedKeepAliveAttempts) { this.failedKeepAliveAttempts = failedKeepAliveAttempts; } // 新增 setter

        public SSHConnection(Session session, ChannelShell channel, InputStream inputStream, OutputStream outputStream, WebSocketSession webSocketSession) {
            this.session = session;
            this.channel = channel;
            this.inputStream = inputStream;
            this.outputStream = outputStream;
            this.webSocketSession = webSocketSession;
            this.connected = true;
        }
    }


    /**
     * 连接SSH (用于WebSocket终端，关联WebSocketSession)
     * @param sessionId 后端生成的SSH连接唯一标识符
     * @param host 服务器地址
     * @param port 端口
     * @param username 用户名
     * @param password 密码
     * @param webSocketSession 关联的WebSocket会话
     * @return 成功建立的SSH连接的sessionId，如果失败则返回null
     */
    public String connectSSH(String sessionId, String host, int port, String username, String password, WebSocketSession webSocketSession) {
        log.info("尝试连接SSH: sessionId={}, host={}, port={}, username={}", sessionId, host, port, username);
        try {
            JSch jsch = new JSch();
            Session session = jsch.getSession(username, host, port);
            session.setPassword(password);
            session.setConfig("StrictHostKeyChecking", "no");

            // ！！！新增：配置SSH协议层面的Keep-alive
            // ServerAliveInterval: 每隔多少秒向服务器发送一次SSH协议层面的心跳包
            // KEEP_ALIVE_INTERVAL_MS 是毫秒，转换为秒使用
            session.setConfig("ServerAliveInterval", String.valueOf(KEEP_ALIVE_INTERVAL_MS / 1000));
            // ServerAliveCountMax: 服务器在多少次心跳无响应后断开连接
            // MAX_FAILED_KEEP_ALIVE_ATTEMPTS 用于此配置
            session.setConfig("ServerAliveCountMax", String.valueOf(MAX_FAILED_KEEP_ALIVE_ATTEMPTS));

            session.connect(30000); // SSH会话连接超时时间

            ChannelShell channel = (ChannelShell) session.openChannel("shell");
            channel.connect(3000); // SSH通道连接超时时间

            InputStream inputStream = channel.getInputStream();
            OutputStream outputStream = channel.getOutputStream();

            SSHConnection connection = new SSHConnection(session, channel, inputStream, outputStream, webSocketSession);
            connections.put(sessionId, connection);

            // 启动一个线程从SSH输入流读取数据并发送到WebSocket
            Thread outputThread = new Thread(() -> {
                try {
                    byte[] buffer = new byte[8192];
                    int i;
                    while ((i = inputStream.read(buffer)) != -1) {
                        if (webSocketSession.isOpen()) {
                            // ！！！恢复到最原始的发送方式，无需过滤 "true"
                            webSocketSession.sendMessage(new TextMessage(new String(buffer, 0, i)));
                            connection.getLastActivity().set(System.currentTimeMillis());
                            // ！！！移除 connection.setFailedKeepAliveAttempts(0); 这一行
                        } else {
                            log.warn("WebSocket会话已关闭，停止转发SSH输出: {}", sessionId);
                            break;
                        }
                    }
                } catch (IOException e) {
                    if (webSocketSession.isOpen()) {
                        log.error("SSH输出读取或WebSocket发送失败: {}", e.getMessage(), e);
                    } else {
                        log.debug("SSH输出线程因WebSocket关闭而停止: {}", sessionId);
                    }
                } finally {
                    disconnectSSH(sessionId);
                }
            });
            outputThread.start();
            connection.setOutputThread(outputThread);

            // !!! 自定义的 Keep-alive 线程在这里被删除，不再有此代码块 !!!

            log.info("SSH连接成功建立: sessionId={}", sessionId);
            return sessionId; // 成功时返回sessionId
        } catch (JSchException | IOException e) {
            log.error("SSH连接失败: sessionId={}, 错误: {}", sessionId, e.getMessage(), e);
            disconnectSSH(sessionId);
            return null; // 失败时返回null
        }
    }
    /*public String connectSSH(String sessionId, String host, int port, String username, String password, WebSocketSession webSocketSession) {
        log.info("尝试连接SSH: sessionId={}, host={}, port={}, username={}", sessionId, host, port, username);
        try {
            JSch jsch = new JSch();
            Session session = jsch.getSession(username, host, port);
            session.setPassword(password);
            session.setConfig("StrictHostKeyChecking", "no");
            session.connect(30000); // SSH会话连接超时时间

            ChannelShell channel = (ChannelShell) session.openChannel("shell");
            channel.connect(3000); // SSH通道连接超时时间

            InputStream inputStream = channel.getInputStream();
            OutputStream outputStream = channel.getOutputStream();

            SSHConnection connection = new SSHConnection(session, channel, inputStream, outputStream, webSocketSession);
            connections.put(sessionId, connection);

            // 启动一个线程从SSH输入流读取数据并发送到WebSocket
            Thread outputThread = new Thread(() -> {
                try {
                    byte[] buffer = new byte[8192];
                    int i;
                    while ((i = inputStream.read(buffer)) != -1) {
                        if (webSocketSession.isOpen()) {
                            webSocketSession.sendMessage(new TextMessage(new String(buffer, 0, i)));
                            connection.getLastActivity().set(System.currentTimeMillis());
                            connection.setFailedKeepAliveAttempts(0);
                        } else {
                            log.warn("WebSocket会话已关闭，停止转发SSH输出: {}", sessionId);
                            break;
                        }
                    }
                } catch (IOException e) {
                    if (webSocketSession.isOpen()) {
                        log.error("SSH输出读取或WebSocket发送失败: {}", e.getMessage(), e);
                    } else {
                        log.debug("SSH输出线程因WebSocket关闭而停止: {}", sessionId);
                    }
                } finally {
                    disconnectSSH(sessionId);
                }
            });
            outputThread.start();
            connection.setOutputThread(outputThread);

            // 启动Keep-alive线程，定期检查SSH连接的活跃度
            Thread keepAliveThread = new Thread(() -> {
                while (!Thread.currentThread().isInterrupted()) {
                    try {
                        Thread.sleep(KEEP_ALIVE_INTERVAL_MS);

                        if (System.currentTimeMillis() - connection.getLastActivity().get() > IDLE_TIMEOUT_MS) {
                            log.info("SSH心跳检测发现连接已空闲超时，主动清理: {}", sessionId);
                            disconnectSSH(sessionId);
                            break;
                        }

                        try {
                            connection.getOutputStream().write("true\n".getBytes());
                            connection.getOutputStream().flush();
                            log.debug("SSH心跳发送成功: {}", sessionId);
                            connection.getLastActivity().set(System.currentTimeMillis());
                            connection.setFailedKeepAliveAttempts(0);
                        } catch (IOException e) {
                            log.warn("SSH心跳发送数据失败（尝试 {}/{})，可能连接已断开: {} - {}",
                                    connection.getFailedKeepAliveAttempts() + 1,
                                    MAX_FAILED_KEEP_ALIVE_ATTEMPTS,
                                    sessionId, e.getMessage());

                            connection.setFailedKeepAliveAttempts(connection.getFailedKeepAliveAttempts() + 1);
                            if (connection.getFailedKeepAliveAttempts() >= MAX_FAILED_KEEP_ALIVE_ATTEMPTS) {
                                log.error("SSH心跳连续失败次数达到上限，强制断开连接: {}", sessionId);
                                disconnectSSH(sessionId);
                                break;
                            }
                        }
                    } catch (InterruptedException e) {
                        log.debug("SSH心跳线程被中断，正在停止: {}", sessionId);
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            });
            keepAliveThread.setDaemon(true);
            keepAliveThread.start();
            connection.setKeepAliveThread(keepAliveThread);

            log.info("SSH连接成功建立: sessionId={}", sessionId);
            return sessionId; // 成功时返回sessionId
        } catch (JSchException | IOException e) {
            log.error("SSH连接失败: sessionId={}, 错误: {}", sessionId, e.getMessage(), e);
            disconnectSSH(sessionId);
            return null; // 失败时返回null
        }
    }*/

    /**
     * 连接SSH (用于测试连接，不关联WebSocketSession)
     * 这种连接建立后应立即断开，仅用于验证凭据和网络可达性。
     */
    public boolean connectSSH(String sessionId, String host, int port, String username, String password) {
        log.info("尝试测试SSH连接: sessionId={}, host={}, port={}, username={}", sessionId, host, port, username);
        Session session = null;
        ChannelShell channel = null;
        try {
            JSch jsch = new JSch();
            session = jsch.getSession(username, host, port);
            session.setPassword(password);
            session.setConfig("StrictHostKeyChecking", "no"); // 不严格检查主机密钥
            session.connect(10000); // 连接超时10秒

            channel = (ChannelShell) session.openChannel("shell");
            channel.connect(5000); // 通道连接超时5秒

            log.info("SSH测试连接成功: {}@{}:{}", username, host, port);
            return true;
        } catch (JSchException e) {
            log.error("SSH测试连接失败: {}@{}:{} - {}", username, host, port, e.getMessage());
            return false;
        } finally {
            // 确保在测试连接后立即断开，释放资源
            if (channel != null) {
                try {
                    channel.disconnect();
                    log.debug("测试连接：Channel已断开");
                } catch (Exception e) {
                    log.warn("测试连接：断开Channel失败: {}", e.getMessage());
                }
            }
            if (session != null) {
                try {
                    session.disconnect();
                    log.debug("测试连接：Session已断开");
                } catch (Exception e) {
                    log.warn("测试连接：断开Session失败: {}", e.getMessage());
                }
            }
        }
    }


    /**
     * 发送命令到SSH终端
     */
    public void sendCommand(String sessionId, String command) {
        SSHConnection connection = connections.get(sessionId);
        if (connection != null && connection.isConnected() && connection.getOutputStream() != null) {
            try {
                connection.getOutputStream().write(command.getBytes());
                connection.getOutputStream().flush();
                connection.getLastActivity().set(System.currentTimeMillis()); // 命令发送时更新最后活动时间
                connection.setFailedKeepAliveAttempts(0); // 命令发送成功，重置心跳失败计数
                log.debug("命令发送成功: {} - {}", sessionId, command.trim());
            } catch (IOException e) {
                log.error("发送命令到SSH终端失败: {} - {}", sessionId, e.getMessage(), e);
                disconnectSSH(sessionId); // 发送失败也断开连接
            }
        } else {
            log.error("SSH连接不存在或已断开，无法发送命令: {}", sessionId);
            // 如果连接已断开但WebSocket仍然开放，尝试通知前端
            if (connection != null && connection.getWebSocketSession() != null && connection.getWebSocketSession().isOpen()) {
                try {
                    connection.getWebSocketSession().sendMessage(new TextMessage("{\"type\":\"error\",\"message\":\"SSH连接已断开，请重新连接。\"}"));
                } catch (IOException ex) {
                    log.error("发送断开消息到WebSocket失败: {}", ex.getMessage());
                }
            }
        }
    }

    /**
     * 断开SSH连接
     */
    public void disconnectSSH(String sessionId) {
        log.info("尝试断开SSH连接: sessionId={}", sessionId);
        SSHConnection connection = connections.remove(sessionId); // 从Map中移除连接
        if (connection != null) {
            if (connection.getChannel() != null) {
                connection.getChannel().disconnect();
                log.info("SSH Channel 已断开: {}", sessionId);
            }
            if (connection.getSession() != null) {
                connection.getSession().disconnect();
                log.info("SSH Session 已断开: {}", sessionId);
            }
            // 中断与此连接相关的线程
            if (connection.getOutputThread() != null && connection.getOutputThread().isAlive()) {
                connection.getOutputThread().interrupt();
                log.info("SSH Output Thread 已中断: {}", sessionId);
            }
//            if (connection.getKeepAliveThread() != null && connection.getKeepAliveThread().isAlive()) {
//                connection.getKeepAliveThread().interrupt();
//                log.info("SSH KeepAlive Thread 已中断: {}", sessionId);
//            }
            connection.setConnected(false); // 更新连接状态
            log.info("SSH连接已完全断开并清理: {}", sessionId);
        } else {
            log.debug("尝试断开不存在的SSH连接: {}", sessionId);
        }
    }

    /**
     * 检查SSH连接是否真的存活 (内部方法)
     * 检查JSch的Session和Channel是否都还连接着
     */
    private boolean isConnectionReallyAlive(SSHConnection connection) {
        boolean connected = connection != null &&
                connection.getSession() != null && connection.getSession().isConnected() &&
                connection.getChannel() != null && connection.getChannel().isConnected();

        // 如果检测到连接已断开，并且该连接关联了WebSocket会话，则清理该连接
        if (!connected) {
            if (connection != null && connection.getWebSocketSession() != null) {
                String webSocketSessionId = connection.getWebSocketSession().getId();
                log.info("检测到连接断开，通过WebSocketSession ID '{}' 清理SSH连接.", webSocketSessionId);
                // 这里调用disconnectSSH来清理，并传入WebSocketSession ID
                // 这将从 connections map中移除条目，并中断相关线程
                disconnectSSH(webSocketSessionId);
            } else {
                log.info("检测到连接断开，但无WebSocketSession可清理 (或连接对象为空): {}", connection != null ? connection.getSession().getUserName() : "未知会话");
            }
        }
        return connected;
    }

    /**
     * 获取连接详细信息
     */
    public Map<String, Object> getConnectionInfo(String sessionId) {
        SSHConnection connection = connections.get(sessionId);
        Map<String, Object> info = new java.util.HashMap<>();

        if (connection != null) {
            boolean reallyAlive = isConnectionReallyAlive(connection);
            info.put("connected", reallyAlive);
            info.put("sessionAlive", connection.getSession() != null && connection.getSession().isConnected());
            info.put("channelAlive", connection.getChannel() != null && connection.getChannel().isConnected());
            info.put("lastActivity", connection.getLastActivity().get());
            info.put("idleTime", System.currentTimeMillis() - connection.getLastActivity().get());
            info.put("hasOutputThread", connection.getOutputThread() != null && connection.getOutputThread().isAlive());
            //info.put("hasKeepAliveThread", connection.getKeepAliveThread() != null && connection.getKeepAliveThread().isAlive());
            info.put("failedKeepAliveAttempts", connection.getFailedKeepAliveAttempts()); // 新增：包含失败尝试次数
            log.debug("获取连接信息: sessionId={}, info={}", sessionId, info);
        } else {
            info.put("connected", false);
            info.put("error", "连接不存在");
            log.debug("获取连接信息: sessionId={}, 连接不存在", sessionId);
        }

        return info;
    }

    /**
     * 检查SSH连接是否处于活动状态 (公共方法)
     * @param sessionId SSH连接的唯一标识符
     * @return 如果连接存在且处于活动状态则返回 true，否则返回 false
     */
    public boolean isConnected(String sessionId) {
        SSHConnection connection = connections.get(sessionId);
        if (connection == null) {
            return false; // 连接不存在
        }
        // 调用内部方法检查连接的真实状态
        return isConnectionReallyAlive(connection);
    }

    /**
     * 调整SSH终端大小
     * @param sessionId SSH连接的唯一标识符
     * @param cols 字符列数
     * @param rows 字符行数
     * @param pixelWidth 像素宽度
     * @param pixelHeight 像素高度
     */
    public void resizeTerminal(String sessionId, int cols, int rows, int pixelWidth, int pixelHeight) {
        SSHConnection connection = connections.get(sessionId);
        if (connection != null && connection.isConnected() && connection.getChannel() != null) {
            try {
                ChannelShell channel = connection.getChannel();
                channel.setPtySize(cols, rows, pixelWidth, pixelHeight);
                log.info("SSH终端大小调整成功: sessionId={}, cols={}, rows={}, pxWidth={}, pxHeight={}",
                        sessionId, cols, rows, pixelWidth, pixelHeight);
            } catch (Exception e) {
                log.error("调整SSH终端大小失败: sessionId={}, 错误: {}", sessionId, e.getMessage(), e);
            }
        } else {
            log.warn("尝试调整SSH终端大小但连接不存在或已断开: sessionId={}", sessionId);
        }
    }
}
